﻿// //   Copyright 2007-2011 Comdiv (F. Sadykov) - http://code.google.com/u/fagim.sadykov/
// //   Supported by Media Technology LTD 
// //    
// //   Licensed under the Apache License, Version 2.0 (the "License");
// //   you may not use this file except in compliance with the License.
// //   You may obtain a copy of the License at
// //    
// //        http://www.apache.org/licenses/LICENSE-2.0
// //    
// //   Unless required by applicable law or agreed to in writing, software
// //   distributed under the License is distributed on an "AS IS" BASIS,
// //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// //   See the License for the specific language governing permissions and
// //   limitations under the License.
// //   
// //   MODIFICATIONS HAVE BEEN MADE TO THIS FILE

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Comdiv.QWeb.Serialization.Extensions;

namespace Comdiv.QWeb.Serialization.Internals {
	internal class XmlSerializerImpl : ISerializerImpl {
		private readonly Stack<XElement> _stack = new Stack<XElement>();
		private XAttribute _currentAttr;
		protected XElement Root;

		private XElement Current {
			get { return _stack.Peek(); }
		}

		#region ISerializerImpl Members

		public TextWriter output { get; set; }

		public void begin(string name) {
			Root = new XElement("root");
			_stack.Push(Root);
		}

		public virtual void end() {
			var e = Root;
			if (e.Elements().Count() == 1) e = e.Elements().First();
			output.Write(e.ToString(SaveOptions.DisableFormatting));
		}

		public void beginObject(string name) {
			if (name.Contains("<")) name = "anonymous";
			if (Regex.IsMatch(name, @"^\d")) {
				name = "item";
			}
			var e = new XElement(name);
			Current.Add(e);
			_stack.Push(e);
		}

		public void endObject() {
			var current = _stack.Pop();
			if (!current.Attributes().Any() && current.Elements().Count() == 1 &&
			    current.Name.LocalName == current.Elements().First().Name.LocalName) {
				current.ReplaceWith(current.Elements().First());
			}
		}

		public void beginObjectItem(string name, bool final) {
			_currentAttr = null;
			if (final) {
				_currentAttr = new XAttribute(name, "");
				Current.Add(_currentAttr);
			}
			else {
				beginObject(name);
			}
		}

		public void endObjectItem(bool last) {
			if (_currentAttr != null) {
				_currentAttr = null;
			}
			else {
				endObject();
			}
		}

		public void writeFinal(object value) {
			if (_currentAttr != null) {
				_currentAttr.SetValue(value ?? "");
			}
			else if (_stack.Count == 1) {
				Current.Add(new XElement("value", value));
			}
			else {
				if (null != value) Current.SetValue(value);
			}
		}

		public void beginDictionary(string name) {
			//empty
		}

		public void endDictionary() {
			//empty
		}

		public void beginDictionaryEntry(string name) {
			var e = new XElement("item", new XAttribute("key", name));
			Current.Add(e);
			_stack.Push(e);
		}

		public void endDictionaryEntry(bool last) {
			_stack.Pop();
		}

		public void beginArray(string name) {
			name = name.Replace("[", "_").Replace("]", "_");
			beginObject(name);
		}

		public void endArray() {
			endObject();
		}

		public void beginArrayEntry(int idx) {
			var e = new XElement("item", new XAttribute("idx", idx));
			Current.Add(e);
			_stack.Push(e);
		}

		public void endArrayEntry(bool last) {
			var e = _stack.Pop();
			if (e.Elements("item").Count() == 1 && e.Elements().Count() == 1) {
				var subst = e.Elements().FirstOrDefault();
				if (subst != null) {
					subst.Add(e.Attribute("idx"));
					e.ReplaceWith(subst);
				}
			}
		}

		public void flush() {
			output.Flush();
		}

		#endregion
	}
}